package com.plugin.manage.interceptor;

import com.plugin.core.bean.LogAop;
import com.plugin.manage.common.BizCode;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import java.io.IOException;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class RepeatSubmitInterceptor implements HandlerInterceptor {

    /**
     * 内存缓存，存储请求记录
     * key: 请求唯一标识, value: 过期时间戳
     */
    private static final ConcurrentHashMap<String, Long> REQUEST_CACHE = new ConcurrentHashMap<>();
    
    /**
     * 定时清理过期记录的线程池
     */
    private static final ScheduledExecutorService CLEANUP_EXECUTOR = Executors.newSingleThreadScheduledExecutor(r -> {
        Thread thread = new Thread(r, "RepeatSubmit-Cleanup");
        thread.setDaemon(true);
        return thread;
    });

    static {
        // 每30秒清理一次过期记录
        CLEANUP_EXECUTOR.scheduleAtFixedRate(() -> {
            long currentTime = System.currentTimeMillis();
            REQUEST_CACHE.entrySet().removeIf(entry -> entry.getValue() < currentTime);
            log.debug("清理过期的防重复提交记录，当前缓存大小: {}", REQUEST_CACHE.size());
        }, 30, 30, TimeUnit.SECONDS);
    }

    /**
     * 防重复提交注解
     */
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface NoRepeatSubmit {
        /**
         * 防重复提交间隔时间，单位秒，默认5秒
         */
        int lockTime() default 5;
        
        /**
         * 提示信息
         */
        String message() default "请勿重复提交";
        
        /**
         * 是否使用Session存储（默认false，使用内存缓存）
         */
        boolean useSession() default false;
    }

    @LogAop(remark = "防重复提交拦截器")
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }

        HandlerMethod handlerMethod = (HandlerMethod) handler;
        NoRepeatSubmit annotation = handlerMethod.getMethodAnnotation(NoRepeatSubmit.class);
        
        if (annotation == null) {
            return true;
        }

        // 生成请求唯一标识
        String requestKey = generateRequestKey(request, handlerMethod);
        
        // 检查是否重复提交
        boolean isRepeat = annotation.useSession() ? 
            isRepeatSubmitBySession(request, requestKey, annotation.lockTime()) :
            isRepeatSubmitByMemory(requestKey, annotation.lockTime());
            
        if (isRepeat) {
            log.warn("检测到重复提交请求: {}", requestKey);
            handleRepeatSubmit(response, annotation.message());
            return false;
        }

        // 记录请求，防止重复提交
        if (annotation.useSession()) {
            recordRequestInSession(request, requestKey, annotation.lockTime());
        } else {
            recordRequestInMemory(requestKey, annotation.lockTime());
        }
        
        return true;
    }

    /**
     * 生成请求唯一标识
     */
    private String generateRequestKey(HttpServletRequest request, HandlerMethod handlerMethod) {
        StringBuilder keyBuilder = new StringBuilder();
        
        // 用户标识
        String userIdentifier = getUserIdentifier(request);
        keyBuilder.append(userIdentifier).append(":");
        
        // 请求路径
        keyBuilder.append(request.getRequestURI()).append(":");
        
        // 请求方法
        keyBuilder.append(request.getMethod()).append(":");
        
        // 方法签名
        keyBuilder.append(handlerMethod.getMethod().getName()).append(":");
        
        // 请求参数
        String params = getRequestParams(request);
        keyBuilder.append(params);
        
        // 使用MD5生成最终的key
        String finalKey = "repeat_submit:" + DigestUtils.md5DigestAsHex(keyBuilder.toString().getBytes());
        log.debug("生成防重复提交key: {}", finalKey);
        
        return finalKey;
    }

    /**
     * 获取用户标识
     */
    private String getUserIdentifier(HttpServletRequest request) {
        // 优先使用用户ID（从token或session中获取）
        String userId = request.getHeader("userId");
        if (userId != null && !userId.isEmpty()) {
            return "user:" + userId;
        }
        
        // 从session中获取用户信息
        HttpSession session = request.getSession(false);
        if (session != null) {
            Object sessionUserId = session.getAttribute("userId");
            if (sessionUserId != null) {
                return "user:" + sessionUserId.toString();
            }
            return "session:" + session.getId();
        }
        
        // 最后使用IP地址
        return "ip:" + getClientIpAddress(request);
    }

    /**
     * 获取客户端IP地址
     */
    private String getClientIpAddress(HttpServletRequest request) {
        String xForwardedFor = request.getHeader("X-Forwarded-For");
        if (xForwardedFor != null && !xForwardedFor.isEmpty() && !"unknown".equalsIgnoreCase(xForwardedFor)) {
            return xForwardedFor.split(",")[0].trim();
        }
        
        String xRealIp = request.getHeader("X-Real-IP");
        if (xRealIp != null && !xRealIp.isEmpty() && !"unknown".equalsIgnoreCase(xRealIp)) {
            return xRealIp;
        }
        
        return request.getRemoteAddr();
    }

    /**
     * 获取请求参数
     */
    private String getRequestParams(HttpServletRequest request) {
        StringBuilder params = new StringBuilder();
        request.getParameterMap().forEach((key, values) -> {
            if (values != null && values.length > 0) {
                params.append(key).append("=").append(values[0]).append("&");
            }
        });
        return params.toString();
    }

    /**
     * 使用内存缓存检查是否重复提交
     */
    private boolean isRepeatSubmitByMemory(String requestKey, int lockTime) {
        Long expireTime = REQUEST_CACHE.get(requestKey);
        if (expireTime == null) {
            return false;
        }
        
        long currentTime = System.currentTimeMillis();
        if (expireTime < currentTime) {
            // 已过期，移除记录
            REQUEST_CACHE.remove(requestKey);
            return false;
        }
        
        return true;
    }

    /**
     * 使用Session检查是否重复提交
     */
    @SuppressWarnings("unchecked")
    private boolean isRepeatSubmitBySession(HttpServletRequest request, String requestKey, int lockTime) {
        HttpSession session = request.getSession(true);
        ConcurrentHashMap<String, Long> sessionCache = 
            (ConcurrentHashMap<String, Long>) session.getAttribute("repeatSubmitCache");
            
        if (sessionCache == null) {
            sessionCache = new ConcurrentHashMap<>();
            session.setAttribute("repeatSubmitCache", sessionCache);
            return false;
        }
        
        Long expireTime = sessionCache.get(requestKey);
        if (expireTime == null) {
            return false;
        }
        
        long currentTime = System.currentTimeMillis();
        if (expireTime < currentTime) {
            // 已过期，移除记录
            sessionCache.remove(requestKey);
            return false;
        }
        
        return true;
    }

    /**
     * 在内存中记录请求
     */
    private void recordRequestInMemory(String requestKey, int lockTime) {
        long expireTime = System.currentTimeMillis() + (lockTime * 1000L);
        REQUEST_CACHE.put(requestKey, expireTime);
        log.debug("记录防重复提交key到内存: {}, 过期时间: {}秒", requestKey, lockTime);
    }

    /**
     * 在Session中记录请求
     */
    @SuppressWarnings("unchecked")
    private void recordRequestInSession(HttpServletRequest request, String requestKey, int lockTime) {
        HttpSession session = request.getSession(true);
        ConcurrentHashMap<String, Long> sessionCache = 
            (ConcurrentHashMap<String, Long>) session.getAttribute("repeatSubmitCache");
            
        if (sessionCache == null) {
            sessionCache = new ConcurrentHashMap<>();
            session.setAttribute("repeatSubmitCache", sessionCache);
        }
        
        long expireTime = System.currentTimeMillis() + (lockTime * 1000L);
        sessionCache.put(requestKey, expireTime);
        log.debug("记录防重复提交key到Session: {}, 过期时间: {}秒", requestKey, lockTime);
    }

    /**
     * 处理重复提交响应
     */
    private void handleRepeatSubmit(HttpServletResponse response, String message) throws IOException {
        response.setStatus(Integer.valueOf(BizCode.DUPLICATE_SUBMMIT.getCode()));
        response.setContentType("application/json;charset=UTF-8");
        
        String jsonResponse = String.format(
            "{\"code\": 429, \"message\": \"%s\", \"data\": null, \"timestamp\": %d}",
            message, System.currentTimeMillis()
        );
        
        response.getWriter().write(jsonResponse);
        response.getWriter().flush();
    }
}
